import json

from PyQt5.QtCore import QPoint, QCoreApplication, Qt
from PyQt5.QtGui import QIcon, QFont, QPixmap

from fznChatUtil import FznChatSender
from login import Ui_Form
from PyQt5 import QtGui, QtNetwork
from PyQt5.QtWidgets import QWidget, QAction, QLineEdit, QMessageBox


class LoginWidget(QWidget, Ui_Form):
    """
    登录窗口

    Attributes:
        server: 服务器地址
        port: 服务器端口
        mainForm: 主窗体
        database: 数据库对象
        network: 网络对象
        log: 日志对象
        isPressedWidget: 控件上是否有鼠标左键按下
        lastPosition: 窗体上一次的位置
        remembered_users: 记录在本地数据库中的用户信息
    """

    def __init__(self, main_form, database, server='127.0.0.1', port='8080', network=None, log=None):
        super().__init__()
        # 服务器地址，用在后续登陆上
        self.server = server
        self.port = port
        # 要跳转的主窗体
        self.mainForm = main_form
        # 存储数据的本地数据库
        self.database = database
        # 网络实例
        self.network = network
        # 日志实例
        self.log = log
        # 拖动窗体需要的变量
        self.isPressedWidget = False
        self.lastPosition = QPoint(0, 0)
        # 用户信息
        self.remembered_users = {}
        # 在是否显示密码之间切换
        self.show_password_action = None
        # 当前用户的头像
        self.current_avatar = None
        # 设置服务器地址的窗体
        # self.setup_server_dialog = SetupServerDialog(self)
        # self.setup_server_dialog.setupUi(self.setup_server_dialog)


    def setupUi(self) -> None:
        """
        设置窗体UI
        :return:
        """
        super().setupUi(self)

        _translate = QCoreApplication.translate
        self.mobileComboBox.lineEdit().setPlaceholderText(_translate("Form", "手机号"))

        # 去掉窗体标题栏，同时也设置为置顶显示
        self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)

        # 手机号码文本框字体设置
        font = QFont("Microsoft YaHei", 11)
        self.mobileComboBox.lineEdit().setFont(font)
        self.mobileComboBox.lineEdit()
        self.mobileComboBox.lineEdit().setPlaceholderText("手机号")

        # 密码输入文本框右侧Action设置
        show_password_action = QAction(self.passwordLineEdit)
        show_password_action.setIcon(QIcon(":/login/resources/images/hidden.png"))
        self.show_password_action = show_password_action
        # 设置QAction的位置在尾部
        self.passwordLineEdit.addAction(show_password_action, QLineEdit.TrailingPosition)
        show_password_action.triggered.connect(self.set_echo_mode)

        # 从数据库中读取记住的用户名和密码，没有的话返回的是一个空的字典
        # remembered_users = self.database.get_remembered_users()
        # self.remembered_users = remembered_users
        # 没有的话返回的是一个空字符串
        # current_user = self.database.get_current_user_mobile()
        # auto_login_user = self.database.get_auto_login_mobile()
        # if len(remembered_users) > 0:
        #     把所有的手机号取出来
        #     user_mobiles = list(remembered_users.keys())
            # 更新下拉框的参数
        #     self.mobileComboBox.addItems(user_mobiles)
        #     # 设置默认选中的下拉项
        #     selected_index = 0
        #     if len(current_user) >= 0:
        #         try:
        #             selected_index = user_mobiles.index(current_user)
        #         except ValueError:
        #             pass
        #     # 设置存储的用户名和密码
        #     self.mobileComboBox.setCurrentIndex(selected_index)
        #     self.passwordLineEdit.setText(remembered_users[user_mobiles[selected_index]])
        #     # 设置checkbox
        #     self.rememberPasswordCheckBox.setChecked(True)
        #     if user_mobiles[selected_index] == auto_login_user:
        #         self.autoLoginCheckBox.setChecked(True)
        #     # 根据存储的用户名获取用户的头像
        #     self.set_avatar(user_mobiles[selected_index])
        # else:
        #     self.mobileComboBox.setCurrentText(current_user)
        #     self.set_avatar(current_user)
        #
        # # 用户切换不同Item的时候，要切换存储的密码和头像
        # self.mobileComboBox.activated.connect(self.set_remembered_password)

    def set_avatar(self, mobile: str) -> None:
        """
        根据传入的用户手机来设置显示的头像。
        :param mobile:
        :return:
        """
        avatar_str, status = self.database.get_common_value("avatar_{}".format(mobile))
        if status == 1:
            full_path = "." + avatar_str
            pixmap = QPixmap(full_path)
            if pixmap.depth() == 0:
                pixmap = QPixmap(':/login/resources/images/user.png')
        else:
            pixmap = QPixmap(':/login/resources/images/user.png')

        pixmap = pixmap.scaled(36, 36, Qt.KeepAspectRatio)
        self.avatarLabel.setPixmap(pixmap)

    def set_remembered_password(self, index: int) -> None:
        """
        每一次组合框选中的时候，要相应改变存储的密码。
        :param index:
        :return:
        """
        mobile = self.mobileComboBox.itemText(index)
        password = self.remembered_users.get(mobile)
        if password:
            self.passwordLineEdit.setText(password)
        self.set_avatar(mobile)

    def set_echo_mode(self) -> None:
        """
        toggle密码框的Echo模式和使用的眼睛图标。
        :return:
        """
        if self.passwordLineEdit.echoMode() == QLineEdit.Password:
            self.passwordLineEdit.setEchoMode(QLineEdit.Normal)
            self.show_password_action.setIcon(QIcon(":/login/resources/images/show.png"))
        else:
            self.passwordLineEdit.setEchoMode(QLineEdit.Password)
            self.show_password_action.setIcon(QIcon(":/login/resources/images/hidden.png"))

    def send_login_msg(self):
        """
        获取用户手机号和密码，向服务器发送登陆信息。
        :return:
        """
        mobile = self.mobileComboBox.currentText()
        password = self.passwordLineEdit.text()

        if self.log:
            self.log.logger.info("发送登陆请求")
        request = self.network.new_request("fznchat/login?mobile={}&password={}".format(mobile, password))
        sender = FznChatSender(self.network, self.log)
        sender.send_msg(request, finished_handler=self.handle_login_response)

    def save_mobile_password(self):
        """
        如果选择了保存用户名、密码和头像，那么保存进数据库。
        :return:
        """
        mobile = self.mobileComboBox.currentText()
        if self.rememberPasswordCheckBox.isChecked():
            self.database.remember_user(mobile, self.passwordLineEdit.text(), self.current_avatar)
        else:
            self.database.forget_user(mobile)

    def handle_login_response(self, reply: QtNetwork.QNetworkReply) -> None:
        """
        根据服务器返回的登陆信息判定登陆是否成功，登陆成功跳转主窗体，失败向用户展示提示信息。服务器返回失败使用的是-1.
        :param reply:服务器响应
        :return:
        """
        self.log.logger.info("处理登陆请求的返回")
        if reply.error() == QtNetwork.QNetworkReply.NoError:
            reply_json_array = json.loads(str(reply.readAll(), 'utf-8'))

            if reply_json_array['code'] == -1:
                QMessageBox.information(self, "财讯通", reply_json_array['msg'])
            else:
                # 保存用户信息到数据库
                if not self.database.set_current_user_mobile(mobile=reply_json_array['data']['mobile']):
                    QMessageBox.critical(self, "财讯通", "无法写入数据到本地数据库")
                else:
                    # 保存自动登录的用户到数据库中
                    if self.autoLoginCheckBox.isChecked():
                        self.database.set_auto_login_mobile(reply_json_array['data']['mobile'])
                    else:
                        # 删除在数据库中记录的自动登录手机号
                        self.database.remove_auto_login_mobile()

                    # 保存当前用户的信息到数据库中
                    self.current_avatar = reply_json_array['data']['avatar']
                    self.save_mobile_password()

                    user_id = reply_json_array['data']['id']

                    self.log.logger.info("切换到主窗口")
                    # 初始化用户表
                    self.mainForm.message_table = FznChatMessageTable(user_id)
                    # 初始化消息免打扰表
                    self.mainForm.do_not_disturb_table = FznChatNotDisturbTable(user_id)
                    # 切换到主窗体
                    self.mainForm.set_login_form(self)
                    # 创建到服务器的WS连接
                    self.mainForm.connect_server(user_id, reply_json_array['data']['token'])
                    # 更新部门信息
                    self.mainForm.update_departments()
                    # 登陆后返回了当前用户的信息，写入数据库中，已经有了需要更新，没有的话就新增
                    self.mainForm.database.user.update_users([reply_json_array['data'], ])
                    # 拉取用户所在的群组
                    self.mainForm.update_groups()
                    # 设置主窗体左上角的图标，不设置的话是默认图标
                    self.mainForm.set_main_icon()
                    # 从数据库中初始化最近消息列表
                    self.mainForm.initial_recent_message_list_widget()
                    # 启动心跳消息
                    self.mainForm.start_heart_beat_thread()
                    self.close()
                    self.mainForm.show()
                    # self.tray.show()
        else:
            QMessageBox.critical(self, "财讯通", "网络错误，无法连接到服务器")

    @staticmethod
    def quit_application():
        """
        退出程序
        :return: None
        """
        QCoreApplication.instance().quit()

    def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:
        """鼠标按下的时候，记录鼠标当前的位置，并为按下鼠标这个行为打上一个标记。

        :param event:鼠标事件
        :return:None
        """
        super().mousePressEvent(event)

        # 当前鼠标按下的即是QWidget，而非界面上布局的其它控件
        self.isPressedWidget = True
        self.lastPosition = event.globalPos()

    def mouseMoveEvent(self, event: QtGui.QMouseEvent) -> None:
        """
        根据已经记录下来的鼠标位置计算鼠标的位移，并根据位移相应的移动窗体的位置。
        :param event:鼠标事件
        :return:
        """
        super().mouseMoveEvent(event)
        self.move_window_by_mouse(event)

    def mouseReleaseEvent(self, event: QtGui.QMouseEvent) -> None:
        """
        移动窗体位置，并重置标记变量。
        :param event:鼠标事件
        :return:
        """
        super().mouseReleaseEvent(event)
        self.move_window_by_mouse(event)
        self.set_mouse_drag_flag()

    def set_mouse_drag_flag(self) -> None:
        """
        初始化鼠标拖拽窗口相关的变量。
        :return:
        """
        self.isPressedWidget = False
        self.lastPosition = QPoint(0, 0)

    def move_window_by_mouse(self, event: QtGui.QMouseEvent) -> None:
        """
        根据用户拖动鼠标来移动窗体。
        :param event:鼠标事件
        :return:
        """
        if self.isPressedWidget:
            # 位置变化量
            dx = event.globalX() - self.lastPosition.x()
            dy = event.globalY() - self.lastPosition.y()

            # 更新位置
            self.lastPosition = event.globalPos()
            # 移动到新位置
            self.move(self.x() + dx, self.y() + dy)

    def auto_login(self):
        """
        用户如果选中了自动登录，那么通过调用这个函数来实现。
        :return:
        """
        if self.autoLoginCheckBox.isChecked():
            self.send_login_msg()

    def keyPressEvent(self, event: QtGui.QKeyEvent):
        if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return:
            self.send_login_msg()
        else:
            super().keyPressEvent(event)
